﻿using ICSharpCode.SharpZipLib.Zip;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;

namespace Common
{
    public class Utils
    {
        public static void UnZip(Stream sm, string zipedFolder)
        {
            using (ZipInputStream s = new ZipInputStream(sm))
            {
                ZipEntry theEntry;
                while ((theEntry = s.GetNextEntry()) != null)
                {
                    string directoryName = Path.Combine(zipedFolder, Path.GetDirectoryName(theEntry.Name));
                    string fileName = Path.GetFileName(theEntry.Name);

                    // create directory
                    if (directoryName.Length > 0 && !Directory.Exists(directoryName))
                    {
                        Directory.CreateDirectory(directoryName);
                    }

                    if (fileName != String.Empty)
                    {
                        using (FileStream streamWriter = File.Create(Path.Combine(directoryName, fileName)))
                        {

                            int size = 2048;
                            byte[] data = new byte[2048];
                            while (true)
                            {
                                size = s.Read(data, 0, data.Length);
                                if (size > 0)
                                {
                                    streamWriter.Write(data, 0, size);
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
        public static string Zip(string sourceDirectory)
        {
            string zipPath = string.Format("{0}.zip", sourceDirectory);
            FastZip fastZip = new FastZip();
            fastZip.CreateZip(zipPath, sourceDirectory, true, "");
            return zipPath;
        }
        public static string WriteNewFile(string path, string context)
        {
            using(FileStream fs = File.Open(path, FileMode.OpenOrCreate))
            {
                byte[] buff = Encoding.UTF8.GetBytes(context);
                fs.Write(buff, 0, buff.Length);
                return path;
            }
        }
        public static string GetFileContent(string path, string encoding="UTF-8")
        {
            if (File.Exists(path))
            {
                using(StreamReader sr = new StreamReader(path, Encoding.GetEncoding(encoding)))
                {
                    return sr.ReadToEnd();
                }
            }
            else
            {
                throw new Exception("file not exists.");
            }
        }

        /// <summary>
        /// 字符串间隔转小写，去掉空格
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string[] TextSplitTrim(string text, string separator, bool toLower=false)
        {
            string[] arry = text.Split(new string[] { separator }, StringSplitOptions.RemoveEmptyEntries);
            if (arry != null && arry.Length > 0)
            {
                for (int i = 0; i < arry.Length; i++)
                {
                    arry[i] = TextTrim(arry[i],toLower);
                }
            }
            //text = string.Join(separator, arry);
            return arry;
        }
        /// <summary>
        /// 字符串转小写,去掉多余的空格
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string TextTrim(string text, bool toLower = false)
        {
            if (string.IsNullOrWhiteSpace(text))
            {
                return "";
            }
            else
            {
                string[] arry = text.Trim().Replace("\n", "").Replace("\r", "").Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                if (toLower)
                {
                    arry = arry.Select(a => a.ToLower()).ToArray();
                }
                text = string.Join(" ", arry);
                return text;
            }
        }
        /// <summary>
        /// 去掉字符串空格 含 Ascii 160 空格
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string TrimSpace(string text)
        {
            byte[] utf8Space = new byte[] { 0xc2, 0xa0 };
            string tempSpace = System.Text.Encoding.GetEncoding("UTF-8").GetString(utf8Space);
            return text.Replace(" ", "").Replace(tempSpace, "").Trim('\0');
        }

        /// <summary>
        /// 通过xsd验证xml格式是否正确，正确返回空字符串，错误返回提示  
        /// </summary>
        /// <param name="xml">xml</param>
        /// <param name="xsd">xsd</param>
        /// <param name="nameSpaceUrl">命名空间，无则默认为null</param>
        /// <returns></returns>
        public static bool XmlValidationByXsd(string xml, string xsd,
            Dictionary<string, int> filedLenthDic, out IList<string> errorMessages, string nameSpaceUrl = null)
        {
            errorMessages = new List<string>();
            IList<string> messages = new List<string>();
            
            using(XmlReader schemaDocument = XmlReader.Create(new StringReader(xsd)))
            {
                XmlReaderSettings settings = new XmlReaderSettings();
                settings.ValidationType = ValidationType.Schema;
                settings.Schemas.Add(nameSpaceUrl, schemaDocument);
                settings.ValidationEventHandler += (x, y) =>
                {
                    string error = y.Message;
                    if (error.Contains("MaxLength"))
                    {
                        int firstIndex = error.IndexOf("'")+1;
                        int length = error.Substring(firstIndex).IndexOf("'");
                        string field = error.Substring(firstIndex, length);

                        if (filedLenthDic.ContainsKey(field))
                        {
                            int maxLength = filedLenthDic[field];
                            error = error.Replace("MaxLength", maxLength.ToString());
                        }
                    }
                    messages.Add(string.Format("{0},LineRow:{1}", error, y.Exception.LineNumber));
                };
                using (XmlReader xmlReader = XmlReader.Create(new StringReader(xml), settings))
                {
                    try
                    {
                        while (xmlReader.Read()) ;
                    }
                    catch (XmlException ex)
                    {
                        messages.Add(string.Format("{0}", ex.Message));
                    }   
                }
            }

            errorMessages = messages;
            return !(messages.Count > 0);
        }
        public static string XmlSerialize<T>(T obj)
        {
            using (StringWriter sw = new StringWriter())
            {
                Type t = obj.GetType();
                XmlSerializer serializer = new XmlSerializer(obj.GetType());
                serializer.Serialize(sw, obj);
                return sw.ToString();
            }
        }
        public static T XmlDeserialize<T>(string xml) where T : class
        {
            try
            {
                using (StringReader sr = new StringReader(xml))
                {
                    XmlSerializer serializer = new XmlSerializer(typeof(T));
                    return serializer.Deserialize(sr) as T;
                }
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        public static bool JsonValidationBySchema(string json, string schema, out IList<string> errorMessages)
        {
            var jSchema = JsonSchema.Parse(schema);
            var jObject = JObject.Parse(json);

            return jObject.IsValid(jSchema, out errorMessages);
        }

        /// <summary>
        /// 获取嵌入资源
        /// </summary>
        /// <param name="type"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public static string GetResourceContent(Type type, string name)
        {
            Assembly assembly = Assembly.GetAssembly(type);
            string projectName = assembly.GetName().Name;
            string resourceName = string.Format("{0}.{1}", projectName, name);

            using (Stream sm = assembly.GetManifestResourceStream(resourceName))
            {
                int numBytesToRead = (int)sm.Length;
                byte[] buffer = new byte[numBytesToRead];
                sm.Read(buffer, 0, buffer.Length);
                return Encoding.UTF8.GetString(buffer);
            }
        }
        /// <summary>
        /// 通用分隔符
        /// </summary>
        public static readonly string GeneralSeparator = "⊙";
        private static readonly MemoryCache cache = new MemoryCache(new MemoryCacheOptions());
        /// <summary>
        /// 获取MemoryCache的值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static T GetMemoryCache<T>(string key)
        {
            T t = (T)cache.Get(key);
            return t;
        }
        public static T GetMemoryCache<T>(string key, Func<T> func, int timeout = 30)
        {
            T t = GetMemoryCache<T>(key);
            if(t == null)
            {
                t = func();
                SetMemoryCache(key, t, timeout);
            }
            return t;
        }
        /// <summary>
        /// 设置MemoryCache
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void SetMemoryCache<T>(string key, T value)
        {
            cache.Set<T>(key, value, new DateTimeOffset(DateTime.UtcNow.AddDays(1)));
        }
        /// <summary>
        /// 设置MemoryCache
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void SetMemoryCache<T>(string key, T value, int timeout)
        {
            if (timeout > 0)
            {
                cache.Set<T>(key, value, new DateTimeOffset(DateTime.UtcNow.AddMinutes(timeout)));
            }
            else
            {
                cache.Set<T>(key, value);
            }
        }
        public static void RemoveMemoryCache(string key)
        {
            cache.Remove(key);
        }
        public static T GetRandomItemFromList<T>(ICollection<T> list)
        {
            T t = default(T);
            if (list != null)
            {
                int maxValue = list.Count;
                Random random = new Random(Guid.NewGuid().GetHashCode());
                int index = random.Next(maxValue);
                t = list.ToList()[index];
            }
            return t;
        }
        /// <summary>
        /// 获取字符串中制定标签的值
        /// </summary>
        /// <param name="str">字符串</param>
        /// <param name="title">标签</param>
        /// <param name="attrib">属性</param>
        /// <returns></returns>
        public static string GetTitleContent(string str, string title, string attrib)
        {
            string pattern = string.Format("<{0}[^>]*?{1}=(['\"\"]?)(?<url>[^'\"\">]+)\\1[^>]*>", title, attrib);
            System.Text.RegularExpressions.Match match = System.Text.RegularExpressions.Regex.Match(str, pattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            string result = match.Groups["url"].Value;
            if (string.IsNullOrEmpty(result))
            {
                result = "";
            }
            return result;
        }
        public static Guid? ConvertGuid(string str)
        {
            if (string.IsNullOrWhiteSpace(str)) return null;
            Guid temp;
            if (Guid.TryParse(str, out temp)) return temp;
            return null;
        }
        public static Guid? ConvertGuid(Guid guid)
        {
            if (guid == Guid.Empty) return null;
            return guid;
        }
        public static string CreateMD5Hash(string input)
        {
            MD5 md5 = MD5.Create();
            byte[] inputBytes = Encoding.ASCII.GetBytes(input);
            byte[] hashBytes = md5.ComputeHash(inputBytes);
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < hashBytes.Length; i++)
            {
                sb.Append(hashBytes[i].ToString("X2"));
            }
            return sb.ToString();
        }
        /// <summary>
        /// GetMD5Hash
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string GetMD5Hash(string input)
        {
            MD5 md5 = MD5.Create();
            byte[] inputBytes = Encoding.ASCII.GetBytes(input);
            byte[] hashBytes = md5.ComputeHash(inputBytes);
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < hashBytes.Length; i++)
            {
                sb.Append(hashBytes[i].ToString("X2"));
            }
            return sb.ToString();
        }
        public static int RandInt()
        {
            Random random = new Random(Guid.NewGuid().GetHashCode());
            return random.Next(1, 10000);
        }
        /// <summary>
        /// 分页查询
        /// </summary>
        /// <param name="field"></param>
        /// <param name="tableName"></param>
        /// <param name="_pagerIndex"></param>
        /// <param name="_pagerSize"></param>
        /// <param name="WhereString"></param>
        /// <param name="_orderString"></param>
        /// <returns></returns>
        public static string GetPageString(string field, string tableName, int _pagerIndex, int _pagerSize, string WhereString, string _orderString)
        {
            StringBuilder sql = new StringBuilder();
            if (string.IsNullOrEmpty(_orderString))//排序不能为空
            {

                _orderString = " id desc";
            }
            sql.Append("(select row_number() over (order by " + _orderString + ") as rowId," + field + "from " + tableName);//查询语句
            if (!string.IsNullOrEmpty(WhereString))
            {
                sql.Append(" where " + WhereString);
            }
            if (_pagerIndex > 0)
            {
                _pagerIndex = _pagerIndex - 1;
            }
            int StartRecord = (_pagerIndex) * _pagerSize + 1;
            int EndRecord = StartRecord + _pagerSize - 1;
            string dd = sql.ToString();
            sql.Clear();
            sql.Append(" select * from " + dd + ") as t where rowId between " + StartRecord + " and " + EndRecord);
            return sql.ToString();
        }
        public static byte[] GetBytes(Stream stream)
        {
            using(MemoryStream mStream = new MemoryStream())
            {
                byte[] bytes = new byte[1024];
                if (stream.CanRead)
                {
                    while (true)
                    {
                        int length = stream.Read(bytes, 0, bytes.Length);
                        if (length <= 0)
                        {
                            break;
                        }
                        mStream.Write(bytes, 0, length);
                    }
                }
                return mStream.ToArray();
            }
        }
        /// <summary>
        /// 压缩文本
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static byte[] Compress(string input)
        {
            if (string.IsNullOrEmpty(input))
            {
                input = "";
            }
            var bytes = Encoding.Unicode.GetBytes(input);
            using (MemoryStream compressStream = new MemoryStream())
            {
                using (var zipStream = new GZipStream(compressStream, CompressionMode.Compress, true))
                {
                    zipStream.Write(bytes, 0, bytes.Length);
                    zipStream.Flush();
                }
           
                return compressStream.ToArray();
            }
        }
        /// <summary>
        /// 解压缩字节
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static string Decompress(byte[] bytes)
        {
            using (var compressStream = new MemoryStream(bytes))
            {
                byte[] block = new byte[1024];
                using (var zipStream = new GZipStream(compressStream, CompressionMode.Decompress, true))
                {
                    using (var resultStream = new MemoryStream())
                    {
                        while (true)
                        {
                            var bytesRead = zipStream.Read(block, 0, block.Length);
                            if (bytesRead <= 0)
                            {
                                break;
                            }
                            else
                            {
                                resultStream.Write(block, 0, bytesRead);
                            }
                        }

                        return Encoding.Unicode.GetString(resultStream.ToArray());
                    }
                }
            }
        }
        /// <summary>
        /// 16进制原码字符串转字节数组
        /// </summary>
        /// <param name="hexString">"AABBCC"或"AA BB CC"格式的字符串</param>
        /// <returns></returns>
        public static byte[] ConvertHexStringToBytes(string hexString)
        {
            hexString = hexString.Replace(" ", "");
            if (hexString.Length % 2 != 0)
            {
                throw new ArgumentException("参数长度不正确");
            }

            byte[] returnBytes = new byte[hexString.Length / 2];
            for (int i = 0; i < returnBytes.Length; i++)
            {
                returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
            }

            return returnBytes;
        }
        /// <summary>
        /// 将数据库二进制数据字符串转换成byte[]
        /// </summary>
        /// <param name="input">0x1F8B0800000000000400ED5D69531B49B</param>
        /// <returns></returns>
        public static byte[] ConvertToBytesFromDataBaseBinaryString(string input)
        {
            return ConvertHexStringToBytes(input.Substring(2));
        }
        public static HttpRequestMessage BuildHttpRequestMessage(string host, string url, EnumHttpMethod httpMethod)
        {
            var request = new HttpRequestMessage(new HttpMethod(httpMethod.ToString()), $"{host}{url}");
            return request;
        }
        public static async Task<string> SendAync(HttpRequestMessage request)
        {
            var client = new HttpClient();
            var httpResponseMessage = await client.SendAsync(request);
            var response = await httpResponseMessage.Content.ReadAsStringAsync();
            return response;
        }
        public static async Task<InvokeApiOutput> InvokeApiAync(string url, string content = "", string authorizationScheme = "", string token = "", string httpMethod = "Get")
        {
            var client = new HttpClient();
            var request = new HttpRequestMessage(new HttpMethod(httpMethod), url);
            if ((httpMethod == HttpMethod.Post.Method || httpMethod == HttpMethod.Patch.Method || httpMethod == HttpMethod.Put.Method)
                && !string.IsNullOrEmpty(content))
            {
                request.Content = new StringContent(content, Encoding.UTF8, "application/json");
            }
            if (!string.IsNullOrEmpty(authorizationScheme)
                && !string.IsNullOrEmpty(token))
            {
                request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(authorizationScheme, token);
            }
            var httpResponseMessage = await client.SendAsync(request);
            var response = await httpResponseMessage.Content.ReadAsStringAsync();

            return new InvokeApiOutput(httpResponseMessage.IsSuccessStatusCode, httpResponseMessage.StatusCode, response);
        }
        /// <summary>
        /// Generate a cryptographic random number.
        /// </summary>
        /// <param name="size"></param>
        /// <returns></returns>
        public static string CreateSalt(int size)
        {
            //Generate a cryptographic random number.
            using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
            {
                byte[] buff = new byte[size];
                rng.GetBytes(buff);

                // Return a Base64 string representation of the random number.
                return Convert.ToBase64String(buff);
            }
        }
        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="key"></param>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string Encrypt(string key, string text)
        {
            var bytes = Encoding.UTF8.GetBytes(text);
            var aes = SetUpAES(key);
            var encryptor = aes.CreateEncryptor();
            var buff = encryptor.TransformFinalBlock(bytes, 0, bytes.Length);
            return Convert.ToBase64String(buff);
        }
        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="key"></param>
        /// <param name="encryptedText"></param>
        /// <returns></returns>
        public static string Decrypt(string key, string encryptedText)
        {
            var bytes = Convert.FromBase64String(encryptedText);
            var aes = SetUpAES(key);
            var decryptor = aes.CreateDecryptor();
            var buff = decryptor.TransformFinalBlock(bytes, 0, bytes.Length);
            return Encoding.UTF8.GetString(buff);
        }
        private static byte[] GenerateAESKey(string key)
        {
            var bytes = Encoding.UTF8.GetBytes(key);
            byte[] encryptionKey = new byte[16];
            for (var i = 0; i < encryptionKey.Length; i++)
            {
                encryptionKey[i] = i > bytes.Length ? (byte)0 : bytes[i];
            }
            return encryptionKey;
        }
        private static SymmetricAlgorithm SetUpAES(string key)
        {
            var aes = Aes.Create();
            aes.Key = GenerateAESKey(key);
            aes.Mode = CipherMode.ECB;
            aes.Padding = PaddingMode.PKCS7;

            return aes;
        }      
    }
}
